package rmi;

import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import lib.Abcast;
import lib.Cbcast;
import lib.Fbcast;
import lib.Protocole;
import client.Client;

/**
 * @author Hsy
 *
 */
public class Groupe {
	/*liste du client*/
	private HashMap<Integer ,Client> listClient;
	private int compteur;
	private int[] list;
	private File logfile;
	private ArrayList<Message> listMsg;
	public static Protocole pt;
	public static final int maxclient=5;
	
	public Groupe(){
		this.listClient = new HashMap<Integer ,Client>();
		compteur = 0;
		list = null;
		logfile = new File("./log/groupe.log");
		listMsg = new ArrayList<Message>();
		try {
			logfile.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	public int enregistreClient(Client cl){
		compteur++;
		if(compteur>maxclient){
			throw new RuntimeException("Le nombre de client est depasse "+maxclient+" !");
		}
		cl.getTb().setId(compteur);
		
		listClient.put(compteur, cl);
		return compteur;
	}

	
	/**
	 * retourner les messages que les clients envoient
	 * @return List de Messages
	 */
	public ArrayList<Message> recvMsg(){	
		return listMsg;
	}

	/**
	 * si Id de l'envoyeur n'egale pas le Client Id de listC , alors ce client est
	 * le recepteur, on vais deliver le message a ce client. Sinon ce client est 
	 * l'envoyeur, donc on le delive pas. 
	 * @param listC listClient qui contient l'identificateur de client
	 * @param msg message qu'on vais envoyer
	 */
	public void recvLog(int[] listC, Message msg){

		if(listC[0]!=msg.getId())
		{
			Log log=new Log(listC,Log.Action.recpt,msg);
			writelog(log.getLogText(),getLogfile());
			System.out.println(log.getLogText());
		}
	}
	

	/**
	 * @param msg
	 * 			 le message pour envoyer
	 * @param flag 
	 * 			 un entier soit 0 soit 1
	 * 			 0: le client envoye le message de dessin
	 * 			 1: le client envoye le message de validation (Abcast)
	 */

	public void envMsg(Message msg,int flag){
		if(flag==0)
		{
			Log log=new Log(null,Log.Action.emmi,msg);
			System.out.println(log.getLogText());
			writelog(log.getLogText(),getLogfile());
			
			if(compteur>1){
				if(pt instanceof Abcast)
				{
				
					pt.lancerThread(msg,getLogfile(),0,0,null);
				}else if(pt instanceof Cbcast)
				{
					pt.init(compteur);
					pt.envMsg(msg);
					MAJClientTAB(msg);
				}else  if(pt instanceof Fbcast)
				{
					pt.init(compteur);
					pt.lancerThread(msg,getLogfile(),0,0,null);
				}
			}
		}
		else
		{
			int length=compteur-1;
			
			ArrayList<Integer> list=new ArrayList<Integer>();
		    pt.lancerThread(msg,null,1,length,list);
			int max=getmax(list);
			Estampille maxi=new Estampille();
			maxi.setEstampille(max);
			msg.setDate(maxi);
			/*
			 * Valider les message et renvoyer a chaque client pour mise a jour
			 * son propre tableux
			 */
			Iterator<Map.Entry<Integer,Client>> iter = listClient.entrySet().iterator(); 
			while (iter.hasNext()) { 
			    Map.Entry<Integer,Client> entry = (Map.Entry<Integer,Client>) iter.next(); 
			    int key = entry.getKey();
			    ClientRmiInterface serv=initContext(key);
			    try {
					serv.update(msg);
					serv.setEstampile(pt.recuMsgs(msg, key).getDate().getEstampille());
				} catch (RemoteException e) {
					
					e.printStackTrace();
				}
		    	
			
			    }		     
			} 
		}
	
	
	public int getmax(ArrayList<Integer> list)
	{
		int max=0,k;
		Iterator<Integer> i=list.iterator();
		while(i.hasNext())
		{
		   k=i.next();	
			if(k>max)
			{
				max=k;
			}
		}
		return max;
	}
	
	public  static ClientRmiInterface initContext(int i)
	{
		Context ctx=null;
		ClientRmiInterface serv=null;
		Hashtable<String,String> env = new Hashtable<String,String>(11);
		    
	    // Definition of the jndi server
    	env.put(Context.INITIAL_CONTEXT_FACTORY,
 			    "com.sun.jndi.rmi.registry.RegistryContextFactory");
 	    env.put(Context.PROVIDER_URL, "rmi://localhost:2999");
 		    
 	    // Get the initial context
	    try {
	    	
	    	ctx = new InitialContext(env);
	    	serv =(ClientRmiInterface) ctx.lookup("cl"+i);
	    	
	        	
			} catch (NamingException e) {
				e.printStackTrace();
			} 
		
		return serv;
	}
	

	public void MAJClientTAB(Message msg){
		list = getListDelivrance(msg.getId(),listClient);
		Log log=new Log(list,Log.Action.delivr,msg);
		System.out.println(log.getLogText());
		writelog(log.getLogText(),getLogfile());
		for(Integer i:list)
		{
			
		    	
			ClientRmiInterface serv=initContext(i);
		    try {
		    	int[] listC = new int[1];
		    	listC[0] = i;
		    	recvLog(listC, msg);
		    	if(pt instanceof Cbcast)
		    	{
			    	//le Protocole de Cbcast
			    	ArrayList<Message> m = pt.recuMsg(msg, i);
			    	for(Message ms :m){
			    		serv.update(ms);
			    	}
		    	}else if(pt instanceof Abcast)
		    	{
			    	//le Protocole de Abcast
			    	serv.update(msg);
			    	serv.setEstampile(pt.recuMsgs(msg, i).getDate().getEstampille());
		    	}
			} catch (RemoteException e) {
				
				e.printStackTrace();
			}
		}    		
	}
	
	/**
	 * retourner la liste de clients qu'on vais delivrer ce message
	 * @param idclient id de client
	 * @return la liste de client
	 */
	public static int[] getListDelivrance(int idclient,HashMap<Integer ,Client> listClient){
		int[] tab = new int[listClient.size()-1];
		int i=0;
		Iterator<Map.Entry<Integer,Client>> iter = listClient.entrySet().iterator(); 
		while (iter.hasNext()) { 
		    Map.Entry<Integer,Client> entry = (Map.Entry<Integer,Client>) iter.next(); 
		    int key = entry.getKey();
		    if(key!=idclient)
		    {
			   	tab[i]=key;
			   	i++;
		    }		     
		} 

		return tab;
	}

	/**
	 * retourner la fichier de log
	 * @return File
	 */
	public File getLogfile() {
		return logfile;
	}
	
	/**
	 * ecrir les logs dans la fichier log
	 * @param log  le nom de fichier log
	 */
	public static void writelog(String log,File logfile)
	{
		
		try {
			
			FileOutputStream fos = new FileOutputStream(logfile,true);
			PrintWriter  pw = new PrintWriter(fos); 
			pw.print(log);
			pw.println();
			pw.close();   
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}		  
	}
	

	/**
	 * definir le type de protocole selon le choix de client
	 * @param choix 
	 * 				un entier
	 */
	public void setProtocole(int choix)
	{
		switch (choix)
		{
			case 0: pt = new Abcast(listClient);break;
			case 1: pt = new Cbcast();break;
			case 2: pt = new Fbcast(listClient);break;
		}	
	}

	public void quit() {
		Iterator<Map.Entry<Integer,Client>> iter = listClient.entrySet().iterator(); 
		while (iter.hasNext()) { 
		    Map.Entry<Integer,Client> entry = (Map.Entry<Integer,Client>) iter.next(); 
		    int key = entry.getKey();
		    ClientRmiInterface serv=initContext(key);
		    try {
				serv.quit();
			} catch (RemoteException e) {
				
				e.printStackTrace();
			}catch(EOFException e)
			{
				System.out.println("le clinet est bien termine");
			}
			
	    	
		
		    }		     
		
	}
}